/**
 * WebSocket Manager
 * Handles all WebSocket connections for real-time communication
 */

class WebSocketManager {
    constructor() {
        this.dataSocket = null;
        this.momentSocket = null;
        this.isDataConnected = false;
        this.isMomentConnected = false;
        this.messageCallbacks = [];
        this.momentCallbacks = [];
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
        this.reconnectInterval = 3000; // 3 seconds
    }

    /**
     * Initialize all WebSocket connections
     */
    init(userId, username) {
        this.userId = userId;
        this.username = username;
        this.connectDataSocket();
        this.connectMomentSocket();
    }

    /**
     * Connect to the data WebSocket for messaging
     */
    connectDataSocket() {
        const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
        const host = window.location.host;
        const url = `${protocol}//${host}/DataWebSocketServer?username=${this.username}`;

        try {
            this.dataSocket = new WebSocket(url);
            
            this.dataSocket.onopen = () => {
                console.log('Data WebSocket connected');
                this.isDataConnected = true;
                this.reconnectAttempts = 0;
                this.triggerCallbacks('data', 'connect', { status: 'connected' });
            };

            this.dataSocket.onmessage = (event) => {
                try {
                    const data = JSON.parse(event.data);
                    console.log('Received data message:', data);
                    this.triggerCallbacks('data', data.type, data);
                } catch (e) {
                    console.error('Error parsing data message:', e);
                }
            };

            this.dataSocket.onclose = (event) => {
                console.log('Data WebSocket closed:', event);
                this.isDataConnected = false;
                this.handleReconnect('data');
            };

            this.dataSocket.onerror = (error) => {
                console.error('Data WebSocket error:', error);
            };
        } catch (error) {
            console.error('Failed to connect to Data WebSocket:', error);
        }
    }

    /**
     * Connect to the moment WebSocket for friends circle
     */
    connectMomentSocket() {
        const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
        const host = window.location.host;
        const url = `${protocol}//${host}/moment/ws?userId=${this.userId}`;

        try {
            this.momentSocket = new WebSocket(url);
            
            this.momentSocket.onopen = () => {
                console.log('Moment WebSocket connected');
                this.isMomentConnected = true;
                this.reconnectAttempts = 0;
                this.triggerCallbacks('moment', 'connect', { status: 'connected' });
            };

            this.momentSocket.onmessage = (event) => {
                try {
                    // If it's a text message
                    if (typeof event.data === 'string') {
                        const data = JSON.parse(event.data);
                        console.log('Received moment message:', data);
                        this.triggerCallbacks('moment', data.type, data);
                    } 
                    // If it's binary data (e.g., image)
                    else if (event.data instanceof Blob) {
                        this.handleBinaryMessage(event.data, 'moment');
                    }
                } catch (e) {
                    console.error('Error parsing moment message:', e);
                }
            };

            this.momentSocket.onclose = (event) => {
                console.log('Moment WebSocket closed:', event);
                this.isMomentConnected = false;
                this.handleReconnect('moment');
            };

            this.momentSocket.onerror = (error) => {
                console.error('Moment WebSocket error:', error);
            };
        } catch (error) {
            console.error('Failed to connect to Moment WebSocket:', error);
        }
    }

    /**
     * Handle binary messages (images, files)
     */
    async handleBinaryMessage(blob, socketType) {
        try {
            // First 4 bytes contain the metadata length
            const headerBuffer = await blob.slice(0, 4).arrayBuffer();
            const metaLength = new DataView(headerBuffer).getInt32(0);
            
            // Extract metadata
            const metaBlob = blob.slice(4, 4 + metaLength);
            const metaText = await metaBlob.text();
            const metadata = JSON.parse(metaText);
            
            // Extract file data
            const fileData = blob.slice(4 + metaLength);
            
            console.log(`Received binary data on ${socketType} socket:`, metadata);
            
            // Create object URL for the file
            const fileUrl = URL.createObjectURL(fileData);
            
            // Trigger callbacks with metadata and file URL
            this.triggerCallbacks(socketType, metadata.type, {
                ...metadata,
                fileUrl,
                blobData: fileData
            });
        } catch (e) {
            console.error(`Error handling binary message on ${socketType} socket:`, e);
        }
    }

    /**
     * Handle WebSocket reconnection
     */
    handleReconnect(socketType) {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            console.log(`Attempting to reconnect ${socketType} socket in ${this.reconnectInterval}ms`);
            this.reconnectAttempts++;
            
            setTimeout(() => {
                if (socketType === 'data') {
                    this.connectDataSocket();
                } else if (socketType === 'moment') {
                    this.connectMomentSocket();
                }
            }, this.reconnectInterval);
        } else {
            console.error(`Failed to reconnect ${socketType} socket after ${this.maxReconnectAttempts} attempts`);
            this.triggerCallbacks(socketType, 'reconnectFailed', {
                attempts: this.maxReconnectAttempts
            });
        }
    }

    /**
     * Send message to the data WebSocket
     */
    sendMessage(recipient, content, type = 'text') {
        if (!this.isDataConnected) {
            console.error('Data WebSocket not connected');
            return false;
        }

        const message = {
            type: type,
            to: recipient,
            content: content
        };

        try {
            this.dataSocket.send(JSON.stringify(message));
            return true;
        } catch (error) {
            console.error('Error sending message:', error);
            return false;
        }
    }

    /**
     * Send binary data to the data WebSocket
     */
    async sendBinaryData(recipient, file, type = 'image') {
        if (!this.isDataConnected) {
            console.error('Data WebSocket not connected');
            return false;
        }

        try {
            const fileData = await this.readFileAsArrayBuffer(file);
            
            // Create metadata
            const metadata = {
                from: this.username,
                to: recipient,
                type: type,
                filename: file.name,
                size: file.size,
                contentType: file.type
            };
            
            const metaString = JSON.stringify(metadata);
            const metaBytes = new TextEncoder().encode(metaString);
            const metaLength = metaBytes.length;
            
            // Create the complete message buffer
            const messageBuffer = new ArrayBuffer(4 + metaLength + fileData.byteLength);
            const view = new DataView(messageBuffer);
            
            // Set metadata length (first 4 bytes)
            view.setInt32(0, metaLength);
            
            // Copy metadata bytes
            new Uint8Array(messageBuffer, 4, metaLength).set(metaBytes);
            
            // Copy file data
            new Uint8Array(messageBuffer, 4 + metaLength, fileData.byteLength)
                .set(new Uint8Array(fileData));
            
            // Send the binary message
            this.dataSocket.send(messageBuffer);
            return true;
        } catch (error) {
            console.error('Error sending binary data:', error);
            return false;
        }
    }

    /**
     * Send moment to the moment WebSocket
     */
    sendMoment(content, mediaUrls = [], visibility = 'FRIENDS') {
        if (!this.isMomentConnected) {
            console.error('Moment WebSocket not connected');
            return false;
        }

        const momentRequest = {
            type: 'CREATE_MOMENT',
            content: content,
            mediaUrls: mediaUrls,
            visibility: visibility
        };

        try {
            this.momentSocket.send(JSON.stringify(momentRequest));
            return true;
        } catch (error) {
            console.error('Error sending moment:', error);
            return false;
        }
    }

    /**
     * Upload media for moments
     */
    async uploadMomentMedia(file) {
        if (!this.isMomentConnected) {
            console.error('Moment WebSocket not connected');
            return false;
        }

        try {
            const fileData = await this.readFileAsArrayBuffer(file);
            
            // Get file extension from filename
            const fileExtension = file.name.split('.').pop().toLowerCase();
            
            // Create metadata
            const metadata = {
                userId: this.userId,
                type: 'MEDIA_UPLOAD',
                fileExtension: fileExtension,
                filename: file.name,
                size: file.size,
                contentType: file.type
            };
            
            const metaString = JSON.stringify(metadata);
            const metaBytes = new TextEncoder().encode(metaString);
            const metaLength = metaBytes.length;
            
            // Create the complete message buffer
            const messageBuffer = new ArrayBuffer(4 + metaLength + fileData.byteLength);
            const view = new DataView(messageBuffer);
            
            // Set metadata length (first 4 bytes)
            view.setInt32(0, metaLength);
            
            // Copy metadata bytes
            new Uint8Array(messageBuffer, 4, metaLength).set(metaBytes);
            
            // Copy file data
            new Uint8Array(messageBuffer, 4 + metaLength, fileData.byteLength)
                .set(new Uint8Array(fileData));
            
            // Send the binary message
            this.momentSocket.send(messageBuffer);
            return true;
        } catch (error) {
            console.error('Error uploading moment media:', error);
            return false;
        }
    }

    /**
     * Finalize media uploads for a moment
     */
    finalizeMomentMediaUploads() {
        if (!this.isMomentConnected) {
            console.error('Moment WebSocket not connected');
            return false;
        }

        const request = {
            type: 'FINALIZE_MEDIA_UPLOADS'
        };

        try {
            this.momentSocket.send(JSON.stringify(request));
            return true;
        } catch (error) {
            console.error('Error finalizing media uploads:', error);
            return false;
        }
    }

    /**
     * Load moments from server
     */
    loadMoments(page = 0, size = 10) {
        if (!this.isMomentConnected) {
            console.error('Moment WebSocket not connected');
            return false;
        }

        const request = {
            type: 'LOAD_MOMENTS',
            page: page,
            size: size
        };

        try {
            this.momentSocket.send(JSON.stringify(request));
            return true;
        } catch (error) {
            console.error('Error loading moments:', error);
            return false;
        }
    }

    /**
     * Like or unlike a moment
     */
    toggleLikeMoment(momentId) {
        if (!this.isMomentConnected) {
            console.error('Moment WebSocket not connected');
            return false;
        }

        const request = {
            type: 'LIKE_MOMENT',
            momentId: momentId
        };

        try {
            this.momentSocket.send(JSON.stringify(request));
            return true;
        } catch (error) {
            console.error('Error toggling like for moment:', error);
            return false;
        }
    }

    /**
     * Comment on a moment
     */
    commentOnMoment(momentId, content) {
        if (!this.isMomentConnected) {
            console.error('Moment WebSocket not connected');
            return false;
        }

        const request = {
            type: 'COMMENT_MOMENT',
            momentId: momentId,
            content: content
        };

        try {
            this.momentSocket.send(JSON.stringify(request));
            return true;
        } catch (error) {
            console.error('Error commenting on moment:', error);
            return false;
        }
    }

    /**
     * Delete a moment
     */
    deleteMoment(momentId) {
        if (!this.isMomentConnected) {
            console.error('Moment WebSocket not connected');
            return false;
        }

        const request = {
            type: 'DELETE_MOMENT',
            momentId: momentId
        };

        try {
            this.momentSocket.send(JSON.stringify(request));
            return true;
        } catch (error) {
            console.error('Error deleting moment:', error);
            return false;
        }
    }

    /**
     * Delete a comment
     */
    deleteComment(commentId) {
        if (!this.isMomentConnected) {
            console.error('Moment WebSocket not connected');
            return false;
        }

        const request = {
            type: 'DELETE_COMMENT',
            commentId: commentId
        };

        try {
            this.momentSocket.send(JSON.stringify(request));
            return true;
        } catch (error) {
            console.error('Error deleting comment:', error);
            return false;
        }
    }

    /**
     * Register callback for WebSocket events
     */
    on(socketType, eventType, callback) {
        if (socketType === 'data') {
            this.messageCallbacks.push({ eventType, callback });
        } else if (socketType === 'moment') {
            this.momentCallbacks.push({ eventType, callback });
        }
    }

    /**
     * Trigger registered callbacks for an event
     */
    triggerCallbacks(socketType, eventType, data) {
        const callbacks = socketType === 'data' ? this.messageCallbacks : this.momentCallbacks;
        callbacks.forEach(item => {
            if (item.eventType === eventType || item.eventType === '*') {
                item.callback(data);
            }
        });
    }

    /**
     * Helper method to read file as ArrayBuffer
     */
    readFileAsArrayBuffer(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = event => resolve(event.target.result);
            reader.onerror = error => reject(error);
            reader.readAsArrayBuffer(file);
        });
    }

    /**
     * Close all WebSocket connections
     */
    closeConnections() {
        if (this.dataSocket && this.isDataConnected) {
            this.dataSocket.close();
        }
        
        if (this.momentSocket && this.isMomentConnected) {
            this.momentSocket.close();
        }
    }
}
const wsManager = new WebSocketManager();
window.wsManager = wsManager; 